Skip to content

[0.9.0] Season seats: setAsideSeasonBlock, retrieveSeasonBlock, releaseSeasonBlock, inventoryMode - #13

Merged
pietechsolution merged 15 commits into
mainfrom
navin/season-block-0.10.0
Nov 11, 2026
Merged

pietechsolution merged 15 commits into
mainfrom
navin/season-block-0.10.0

Conversation

@pietechsolution

@pietechsolution pietechsolution commented Nov 10, 2026 •

Copy link
Copy Markdown
Contributor

Season seats for seatlayer/seatlayer-php, part of the 0.9.0 release. One release, 0.9.0, carries season 2a (#12) and this PR (decided 10 Nov).

  • Versions: old 0.8.0 (published) → new 0.9.0 (with season-2a, Season 2a server operations (part A) #12). Nothing is published yet: Packagist stops at v0.8.0 (checked 10 Nov).
  • 0.8.0 has the 48 season operations from 0.7.0/0.8.0. It has none of the season-2a calls and none of these.
  • The branch is cut from season-2a-server so it builds on the season-2a methods. Its name still says 0.10.0, from before the decision: renaming a head branch would close the PR.
  • No tag is in this PR. composer.json has no version, by design: on Packagist a tag is the publish.

What it adds

Built to docs/season-block-2026-11-08/contract.md in the app repo (§2, §5, §6, and §8 "Amendments, 9 Nov"). Method names are the operationIds.

  • Methods:

    • $seasons->setAsideSeasonBlock($seasonKey, ['expectedRevision' => …, 'keepForSingleTickets' => [...]], $idempotencyKey);
    • $seasons->retrieveSeasonBlock($seasonKey);
    • $seasons->releaseSeasonBlock($seasonKey, ['expectedRevision' => …, 'scope' => [...], 'eventKeys' => [...], 'at' => …, 'reason' => …], $idempotencyKey).

    Both writes use header replay, so an Idempotency-Key is generated when none is given, as on refundSeasonPerformance. Each write answers job and block; follow the job with retrieveSeasonBulkJob(). The array shapes are typed for PHPStan.

  • inventoryMode (block | per_hold) on createSeason() and updateSeason(), in the typed array shapes and sent only when given. block opts the season into season seats; the default stays per_hold. Every Season answer carries it.

  • The 202 while a date leaves every season seat (createSeasonAmendment cancel, markSeasonPerformancePast): Seasons::isDateLeavePending($answer). The return types are unchanged.

  • Webhooks: Webhooks sends any event name, so season.block.ready and season.block.released need no change. The CHANGELOG names them.

  • Errors: no new classes or constants. The new codes come through SeatLayerException and its subclasses with errorCode (§8 item 4). The CHANGELOG lists them, now including every code the 3 calls and inventoryMode can send (SEASON_BLOCK_API_BUSINESS_ERRORS in the app repo).

  • Docs: a "Season seats" section at the end of the README's "Fixed Renewable Seasons": opt in with inventoryMode, set the seats aside, follow the job, read the block, release to single sale (now or at a set time), the two webhook names, the 202 guard, refusals through SeatLayerException with errorCode, and partner seats staying out of the block.

  • CHANGELOG: the "Next: 0.9.0" section. The version PR joins it with the season-2a section under ## 0.9.0.

Files changed

CHANGELOG.md, README.md, src/Resources/Seasons.php, tests/SeasonTest.php (4 files, +266 −9).

Checks

  • The repo's gate, re-run 10 Nov on this commit:

    • vendor/bin/phpstan analyse (level 8): no errors;
    • vendor/bin/phpunit: 77/77, 468 assertions.

    This ran on PHP 8.5 locally (CI runs 8.1–8.4). PHPStan needs --memory-limit=1G here; that is this machine's php.ini, not the code.

    • The operation map test goes 87 → 90: paths, exact bodies, the caller's key and the generated replay key.
    • A test sends the phase 4 answers through the transport: the 202s, a job of a new kind, and the 409 codes, each a ConflictException with its errorCode.
    • inventoryMode on create and update.
  • Mutation check (run before this amend; the code is unchanged since): 7 mutants (the 3 paths, header replay, the caller's key, the guard's two halves): 7/7 killed.

  • The README sample isn't run by the repo. Its syntax was checked once (php -l), and the calls by hand against the signatures and PHPStan shapes in Seasons.php.

  • Compat matrix: the same fixtures, shaped as the phase 4 API answers, through a stub transport. The script is season-phase4-php-compat.php in the app session's notes; nothing is sent.

Build Result
published 0.8.0 (Packagist, composer require seatlayer/seatlayer-php:0.8.0, re-run 10 Nov) all PASS where the method exists, 4 of 14: the 409 code and the 202 on createSeasonAmendment, a Season answer carrying inventoryMode, and 409 season_block_packages on updateSeason. The other 10 are n/a: 0.8.0 has neither the season-2a calls nor these
season-2a-server head 62bf35d all PASS (10 Nov run, not re-run): jobs of both new kinds, a mixed job list, the three new 409 codes, the 202 on a cancel and on a past date, inventoryMode and 409 season_block_packages
this build (0.9.0) all PASS, 14/14 (re-run 10 Nov on this commit's src/), including the 3 new calls and 409 season_block_not_enabled
  • Not done yet: a live dev smoke of the 3 calls with the dev test key. The phase 4 API isn't on dev until the stack merges; it is step 3 below.

Release steps for the owner

  1. The season-2a PR (Season 2a server operations (part A) #12) merges to main, with no tag.

  2. Rebase this PR onto main and retarget it to main; the gate green, and composer validate --strict; merge.

  3. Compat matrix again, once the phase 4 API is on dev:

    • run the compat script against Packagist 0.8.0 and against main's src/: both all PASS;
    • the live dev smoke of the 3 calls with the dev test key: opt in, set aside, follow the job to completed, read, release with at, read the release job.
  4. Version PR: there is no version field. Join the CHANGELOG's season-2a section ("Unreleased") and "Next: 0.9.0" under a dated ## 0.9.0. Merge to main.

  5. Publish: only after the phase 4 API (paiteq/seatmap #2059 → #2086 → #2061 → #2062 → #2063 → #2066 → #2092) is live on production and the owner says yes: tag the merge commit, git tag v0.9.0 && git push origin v0.9.0. Check git rev-list -n1 v0.9.0 equals git rev-parse main first (RELEASE.md §2.0): a tag on the wrong commit publishes the wrong tree.

  6. Live re-check:

  7. Rollback (0.9.0 only adds). Packagist has no unlist (RELEASE.md §5): it mirrors the tags.

    • Already installed by anyone: leave v0.9.0, ask users to pin 0.8.0, and tag the fix as v0.9.1.
    • Never installed: delete the v0.9.0 tag on GitHub, then delete the version on Packagist. After real installs this breaks anyone who pinned it.

    Either choice covers season-2a too: both ship in 0.9.0.

Failure modes

  • Publishing before the phase 4 API is on production: the 3 calls would answer 404, and inventoryMode would be ignored. Step 5 waits for production.
  • A tag on the wrong commit: on Packagist the tag is the publish, so step 5 checks the tag's commit first.
  • A caller skipping Seasons::isDateLeavePending(): it would read the 202 body as the amendment. The README and the doc comments point at the guard.
  • Old SDKs: 0.8.0 reads the new answers through its generic exception and ignores inventoryMode (compat matrix). It can't call the 3 new routes.
  • README drift: the sample isn't run. A later signature change would leave it stale.

Open risks

  • No live dev smoke yet (step 3).
  • The README links to the docs' "Season seats" section (/server-api/seasons/#season-seats), which goes live with paiteq/seatmap #2063. Until then the link lands on the top of the Seasons page.
  • The README's season intro still counts 87 operations (it is 90 with this PR); it was left as it was.

…tle (2.16c)

createEventSeries takes fromEventKey: an event already set up becomes the first date under its own key and every other date is a copy of it; name and chartKey are then left out. Every date of a series carries the series' plain name. Matches seatmap slice 2.16c.
Adds releaseSeasonLapsedOffers (POST /v1/seasons/{key}/renewal-offers/release-lapsed, no body, header replay, 202 with the job). retrieveSeasonHostedRenewals pages with cursor and limit (1-500) and answers nextCursor, and listSeasonRenewalOffers pages the same way (1-100). createSeasonRenewalOffers answers 201 {offers} for 100 contracts or fewer and 202 {job, batchId} above that. The bring-across (createSeasonHolderImportFromSeason) answers 202 with a holder_import job above 100 holders. Renewal emails take up to 20,000 offer ids; above that, 422 too_many_offer_ids.
…e_abandoned (p2b)

The public API changes in p2b (b88ed794e..ee7df2921): Seasons::dateCancelVoidJobId names the date_cancel_void job a date cancel starts; Seasons::holderImportRowsPerCall sizes an import call by its dates (a call over it answers 422 invalid_rows with maxRows); updateSeason documents singleTicketDates.jobRefused; createEventSeries documents 409 create_abandoned.
…b audit)

updateEventSeries sends only the prices that change: each date keeps its
own price for every category or tier left out. A season's null transfer
or return cut-off means the club default from Settings > Ticket site, or
until the date starts when the club has none. Holder transfers and
returns through the secret key are not stopped by the club's customer
switches; cut-offs still apply.
…part C)

A date its season cancelled never opens again: moving its sales to
testable or live, or reopening it, answers 409 season_date_cancelled,
and an event series "sales on" leaves it closed as a failed item of the
job (season_date_cancelled; not in failedDates). A cancelled date can't
be moved, postponed, replaced or marked as taken place: 409
performance_cancelled.
@pietechsolution
pietechsolution force-pushed the navin/season-block-0.10.0 branch 2 times, most recently from 5c50999 to 8cf1533 Compare November 10, 2026 02:32
…seSeasonBlock; Seasons::isDateLeavePending for the 202 date-leave answer. No tag: publish only once the phase 4 API is on production
@pietechsolution
pietechsolution force-pushed the navin/season-block-0.10.0 branch from 8cf1533 to cc655a8 Compare November 10, 2026 05:48
@pietechsolution pietechsolution changed the title [0.10.0, NOT for 0.9.0] Season seats: setAsideSeasonBlock, retrieveSeasonBlock, releaseSeasonBlock [0.9.0] Season seats: setAsideSeasonBlock, retrieveSeasonBlock, releaseSeasonBlock, inventoryMode Nov 10, 2026
@pietechsolution pietechsolution mentioned this pull request Nov 11, 2026
@pietechsolution
pietechsolution changed the base branch from season-2a-server to main November 11, 2026 10:14
@pietechsolution
pietechsolution marked this pull request as ready for review November 11, 2026 10:14
@pietechsolution
pietechsolution merged commit 2727f17 into main Nov 11, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants